<?php 

class SemanticAnalyser
{
	var $knownKeywordWeights = array();
	var $keywords = array();
	var $keywordTypes = array();

	var $kc = 0;
	var $keywordCollection = array();

	var $nc = 0;
	var $nounCollection = array();
	
	var $maxWidth = 0;
	
	private function tokenize($string) {
		$string = strtolower($string);
		$valid = str_split('abcdefghijklmnopqrstuvwxyz0123456789 ');
		$remove = array();
		
		for ($i = 0; $i < strlen($string); $i++) {
			if (!in_array($string[$i], $valid)) $remove[] = $string[$i];
		}
		
		$string = str_replace($remove, '', $string);
		echo $string,"\n";
		
		return explode(' ', $string);
	}
	
	public function readScript($filename, $defaultWidth)
	{
		$lines = file($filename);
		foreach ($lines as $line) {
			$words = preg_split('/\s+/',$line, -1, PREG_SPLIT_NO_EMPTY);
			
			$i = 0;
			while (array_key_exists($i, $words))
			{
				if ($words[$i] == ":" || $words[$i] == "+" || $words[$i] == "-" || $words[$i] == "*") break;
				$i++;
			}

			if ($i < count($words))
			{
				if ($words[$i] == "+")
				{
					$keywords = array();
					$ki = 0;
					for ($k = 0; $k < $i; $k++)
					{
						if (is_numeric($words[$k + 1]))
						{
							$keywords[$words[$ki++]] = (int) $words[++$k];
						}
						else
							$keywords[$words[$ki++]] = $defaultWidth;
					}
					
					if (array_key_exists($i + 1, $words))
					{
						$this->addValue($keywords, (float) $words[$i + 1]);
					}
				}
				else if ($words[$i] == "-")
				{
					$keywords = array();
					$ki = 0;
					for ($k = 0; $k < $i; $k++)
					{
						if (is_numeric($words[$k + 1]))
						{
							$keywords[$words[$ki++]] = (int) $words[++$k];
						}
						else
							$keywords[$words[$ki++]] = $defaultWidth;
					}
					
					if (array_key_exists($i + 1, $words))
					{
						$this->addValue($keywords, 0 - (float) $words[$i + 1]);
					}
				}
				if ($words[$i] == "*")
				{
					$keywords = array();
					$ki = 0;
					for ($k = 0; $k < $i; $k++)
					{
						if (is_numeric($words[$k + 1]))
						{
							$keywords[$words[$ki++]] = (int) $words[++$k];
						}
						else
							$keywords[$words[$ki++]] = $defaultWidth;
					}
					
					if (array_key_exists($i + 1, $words))
					{
						$this->addMultiplier($keywords, (float) $words[$i + 1]);
					}
				}
				else
				{
					$keywords = array();						
					if (is_numeric($words[$k + 1]))
						{
							$keywords[$words[$ki++]] = (int) $words[++$k];
						}
						else
							$keywords[$words[$ki++]] = $i - $k - 1;
					
					$nouns = array();
					for ($k = $i + 1; array_key_exists($k + 1, $words); $k += 2)
					{
						$value = (float) $words[$k];
						$nouns[$words[$k + 1]] = $value;
					}
					
					$this->addNouns($keywords, $nouns);
				}
			}
		}
	}

	private function linkKC($keyword, $weight, $kc)
	{
		if (!array_key_exists($keyword . "_" . $weight, $this->keywords))
		{
			$i = 0;
			while (array_key_exists($keyword . "_" . $i, $this->knownKeywordWeights)) $i++;
			$this->knownKeywordWeights[$keyword . "_" . $i] = $weight;
			$count = 0;
		}
		else
		{
			$count = $this->keywords[$keyword . "_" . $weight] % 16;
		}
		$this->keywords[$keyword . "_" . $weight . "_" . $count] = $kc;
		
		$this->keywords[$keyword . "_" . $weight] = ++$count;
	}

	public function addMultiplier($keywordsWithWeight, $amount)
	{
		$amount = (int)(($amount + 128) * 128);
		
		$this->kc++;
		$this->keywordCollection[$this->kc] = ($amount << 16) + (0 << 8) + count($keywordsWithWeight);
		
		foreach ($keywordsWithWeight as $keyword => $weight)
		{
			if ($weight > $this->maxWidth) $this->maxWidth = $weight;
			$this->linkKC($keyword, $weight, $this->kc);
		}
	}

	public function addValue($keywordsWithWeight, $amount)
	{
		$amount = (int)(($amount + 128) * 128);
		
		$this->kc++;
		$this->keywordCollection[$this->kc] = ($amount << 16) + (1 << 8) + count($keywordsWithWeight);
		
		foreach ($keywordsWithWeight as $keyword => $weight)
		{
			if ($weight > $this->maxWidth) $this->maxWidth = $weight;
			$this->linkKC($keyword, $weight, $this->kc);
		}
	}

	public function addNouns($keywordsWithWeight, $nouns)
	{
		$this->nc++;
		$this->nounCollection[$this->nc] = $nouns;

		$this->kc++;
		$this->keywordCollection[$this->kc] = ($this->nc << 16) + (2 << 8) + count($keywordsWithWeight);

		foreach ($keywordsWithWeight as $keyword => $weight)
		{
			if ($weight > $this->maxWidth) $this->maxWidth = $weight;
			$this->linkKC($keyword, $weight, $this->kc);
		}
	}

	var $changeBuffer_words_s = array();
	var $changeBuffer_weights_s = array();
	var $changeBuffer_words_e = array();
	var $changeBuffer_weights_e = array();
	var $changeBuffer_words_n = array();

	public function parse($tokens)
	{
		$tokens = $this -> tokenize($tokens);
		$m = ($this->maxWidth * 2) + 1;
		
		$results = array();
	
		$multiplier = 1;
		$value = 0;

		for ($i = 0; $i < $m; $i++)
		{
			$this->changeBuffer_words_s[$i] = array();
			$this->changeBuffer_weights_s[$i] = array();
			$this->changeBuffer_words_e[$i] = array();
			$this->changeBuffer_weights_e[$i] = array();
			$this->changeBuffer_words_n[$i] = array();
		}
		
		$x = 0;
		$y = 0;
		$tokenCount = count($tokens);
		$xystart = min($tokenCount, $this->maxWidth);
		
		
		while ($y < $tokenCount)
		{
			if ($x < $tokenCount)
			{
				$token = $tokens[$x];
			
				$wi = 0;
				while (true)
				{
					if (!array_key_exists($token . "_" . $wi, $this->knownKeywordWeights)) break;
					$w = $this->knownKeywordWeights[$token . "_" . $wi];
					
					$indexS = max(0, $x - $w) % $m;
					$indexE = min($tokenCount-1, $x + $w) % $m;
					
					$countS = count($this->changeBuffer_words_s[$indexS]);
					$countE = count($this->changeBuffer_words_e[$indexE]);
					
					if ($indexE != $indexS)
					{
						$this->changeBuffer_words_s[$indexS][$countS] = $token;
						$this->changeBuffer_weights_s[$indexS][$countS] = $w;
					
						$this->changeBuffer_words_e[$indexE][$countE] = $token;
						$this->changeBuffer_weights_e[$indexE][$countE] = $w;
					}
					else
					{
						$this->changeBuffer_words_n[$indexS][$countS] = $token;
					}

					$wi++;
				}
				
				$x++;
			}
			
			if ($x >= $xystart)
			{
				$ym = $y % $m;
				
				for ($phase = 0; $phase < 4; $phase++)
				{
					$i = 0;
					while (true)
					{
						if ($phase == 0)
						{
							if (!array_key_exists($i, $this->changeBuffer_words_s[$ym])) break;
							$word = $this->changeBuffer_words_s[$ym][$i];
							$weight = $this->changeBuffer_weights_s[$ym][$i];
							$type = true;
						}
						else if ($phase == 1)
						{
							if (!array_key_exists($i, $this->changeBuffer_words_n[$ym])) break;
							$word = $this->changeBuffer_words_n[$ym][$i];
							$weight = 0;
							$type = true;
						}
						else if ($phase == 2)
						{
							if (!array_key_exists($i, $this->changeBuffer_words_n[$ym])) break;
							$word = $this->changeBuffer_words_n[$ym][$i];
							$weight = 0;
							$type = false;
						}
						else
						{
							if (!array_key_exists($i, $this->changeBuffer_words_e[$ym])) break;
							$word = $this->changeBuffer_words_e[$ym][$i];
							$weight = $this->changeBuffer_weights_e[$ym][$i];
							$type = false;
						}

						if ($type)
						{
							$kstats = $this->keywords[$word . "_" . $weight];
							$active = $kstats >> 16;
							$count = $kstats % 16;

							if ($active++ == 0)
							{
								for ($c = 0; $c < $count; $c++)
								{
									$kci = $this->keywords[$word . "_" . $weight . "_" . $c];
									$thekc = $this->keywordCollection[$kci];

									$kcAmount = $thekc >> 16;
									$kcType = ($thekc >> 8) % 8;
									$kcSize = $thekc % 8;

									if (--$kcSize == 0)
									{
										if ($kcType == 0)
										{
											$multiplier *= $kcAmount / 128.0 - 128;
										}
										else if ($kcType == 1)
										{
											$value += $kcAmount / 128.0 - 128;
										}
										else if ($kcType == 2)
										{
											foreach ($this->nounCollection[$kcAmount] as $noun => $nounMultiplier)
											{
												if (!array_key_exists($noun, $results)) $results[$noun] = 0;
												$results[$noun] += $nounMultiplier * $multiplier * $value;
											}
										}
									}
									$this->keywordCollection[$kci] = ($kcAmount << 16) + ($kcType << 8) + $kcSize;
								}
							}

							$this->keywords[$word . "_" . $weight] = ($active << 16) + $count;
						}
						else
						{
							$kstats = $this->keywords[$word . "_" . $weight];
							$active = $kstats >> 16;
							$count = $kstats % 16;

							if (--$active == 0)
							{
								for ($c = 0; $c < $count; $c++)
								{
									$kci = $this->keywords[$word . "_" . $weight . "_" . $c];
									$thekc = $this->keywordCollection[$kci];

									$kcAmount = $thekc >> 16;
									$kcType = ($thekc >> 8) % 8;
									$kcSize = $thekc % 8;

									if ($kcSize++ == 0)
									{
										if ($kcType == 0)
										{
											$multiplier /= $kcAmount / 128 - 128;
										}
										else if ($kcType == 1)
										{
											$value -= $kcAmount / 128 - 128;
										}
									}
									$this->keywordCollection[$kci] = ($kcAmount << 16) + ($kcType << 8) + $kcSize;
								}
							}

							$this->keywords[$word . "_" . $weight] = ($active << 16) + $count;
						}

						$i++;
					}
				}
				
				$this->changeBuffer_words_s[$ym] = array();
				$this->changeBuffer_weights_s[$ym] = array();
				$this->changeBuffer_words_e[$ym] = array();
				$this->changeBuffer_weights_e[$ym] = array();
				$this->changeBuffer_words_n[$ym] = array();
				
				$y++;
			}
		}
		
		return $results;
	}
}

/*$sa = new SemanticAnalyser();
$sa->readScript("semantic_rules.txt", 10);

$words = "i think marks and spencers is not very good";

$result = $sa->parse($words);
$result = $sa->parse($words);

echo "\n";

foreach ($result as $company => $amount)
{
	echo "$company => $amount\n";
}*/

?>